package com.mxs.qrcode.utils;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import org.junit.Test;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

public class QrCodeUtil {

    @Test
    public void test(){
        String content = "即使爬到最高的山上,一次也只能脚踏实地地迈一步。";
        String path = "D:/QrCodeUtil/";


        Color startColor = new Color(234, 38, 1);
        Color endColor = new Color(16, 222, 9);
        Color leftTopExternalColor = new Color(231, 144, 56);
        Color leftTopInteriorColor = new Color(21, 144, 56);
        Color rightTopExternalColor = new Color(32, 131, 236);
        Color rightTopInteriorColor = new Color(21, 144, 56);
        Color leftBottomExternalColor = new Color(199, 119, 255);
        Color leftBottomInteriorColor = new Color(21, 144, 56);
        Color impressionColor = new Color(16, 16, 15);

        QrCodeUtil.encode(content,500,500,path);
        QrCodeUtil.encode(content,500,500,path,startColor,endColor);
        QrCodeUtil.encode(content,500,500,path,startColor,endColor
                ,leftTopExternalColor,leftTopInteriorColor,rightTopExternalColor,rightTopInteriorColor
                ,leftBottomExternalColor,leftBottomInteriorColor,impressionColor);

        /*String imgPath = "D:/QrCodeUtil/3.jpg";

        QrCodeUtil.encode(content,path,imgPath);*/
    }

    private static String encode(String content, String path,String imgPath) {
        try {
            String fileName = UUID.randomUUID()+".png";
            File file = new File(path+"/"+fileName);

            BufferedImage bufferedImage = genBarcode(content,imgPath);
            ImageIO.write(bufferedImage,"png", file);
            return fileName;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }



    public static String encode(String content, int width, int height,String path) {
        return encode(content,width,height,path,null,null);
    }

    private static String encode(String content, int width, int height, String path, Color startColor, Color endColor) {
        return encode(content,width,height,path,startColor,endColor,null,null,null,null,null,null,null);

    }
    private static String encode(String content, int width, int height, String path, Color startColor, Color endColor
            , Color leftTopExternalColor, Color leftTopInteriorColor
            , Color rightTopExternalColor, Color rightTopInteriorColor
            , Color leftBottomExternalColor, Color leftBottomInteriorColor
            ,Color impressionColor) {
        try {
            String fileName = UUID.randomUUID()+".png";
            File file = new File(path+"/"+fileName);

            BufferedImage bufferedImage = genBarcode(content, width, height,startColor,endColor
                    ,leftTopExternalColor,leftTopInteriorColor,rightTopExternalColor,rightTopInteriorColor
                    ,leftBottomExternalColor,leftBottomInteriorColor,impressionColor);
            ImageIO.write(bufferedImage,"png", file);
            return fileName;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    private static BufferedImage genBarcode(String content, String imgPath) throws WriterException {
        Map<EncodeHintType, Object> hint = new HashMap<EncodeHintType, Object>();
        hint.put(EncodeHintType.CHARACTER_SET, "utf-8");
        hint.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);

        File file = new File(imgPath);
        BufferedImage bi = null;
        try {
            bi = ImageIO.read(file);
        } catch (Exception e) {
            e.printStackTrace();
        }
        int width = bi.getWidth();
        int height = bi.getHeight();
        // 生成二维码
        MultiFormatWriter mutiWriter = new MultiFormatWriter();

        BitMatrix matrix = mutiWriter.encode(content, BarcodeFormat.QR_CODE, width, height, hint);
        int w = matrix.getWidth();
        int h = matrix.getHeight();
        int[] data = new int[w * h];



        for (int y = 0; y < h; y++) {
            for (int x = 0; x < w; x++) {
                int colorInt = 0;
                if(matrix.get(x, y)){
                    try{
                        colorInt = bi.getRGB(x, y);
                    }catch (Exception e){
                        colorInt = 0;
                    }
                }else{
                    colorInt = -1;
                }
                data[y * w + x] = colorInt;
            }
        }

        BufferedImage image = new BufferedImage(width, height,
                BufferedImage.TYPE_INT_RGB);
        image.getRaster().setDataElements(0, 0, width, height, data);
        return image;

    }
    /**
     * 生成二维码
     * @param content
     * @param width
     * @param height
     * @param startColor  渐变起始颜色
     * @param endColor    渐变结束颜色
     * @param leftTopExternalColor 左上角码眼颜色
     * @param leftTopInteriorColor 左上角内码眼颜色
     * @param rightTopExternalColor 右上角码眼颜色
     * @param rightTopInteriorColor 右上角内码眼颜色
     * @param leftBottomExternalColor 左下角码眼颜色
     * @param leftBottomInteriorColor 左下角内码眼颜色
     * @param impressionColor 底部颜色
     * @return
     * @throws WriterException
     */
    private static BufferedImage genBarcode(String content, int width, int height
            ,Color startColor,Color endColor
            ,Color leftTopExternalColor ,Color leftTopInteriorColor
            ,Color rightTopExternalColor ,Color rightTopInteriorColor
            ,Color leftBottomExternalColor ,Color leftBottomInteriorColor,Color impressionColor) throws WriterException {
        Map<EncodeHintType, Object> hint = new HashMap<EncodeHintType, Object>();
        hint.put(EncodeHintType.CHARACTER_SET, "utf-8");
        hint.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
        // 生成二维码
        MultiFormatWriter mutiWriter = new MultiFormatWriter();

        BitMatrix matrix = mutiWriter.encode(content, BarcodeFormat.QR_CODE, width, height, hint);
        int w = matrix.getWidth();
        int h = matrix.getHeight();
        int[] data = new int[w * h];


        int[] attr = getLeftTopCodeEyeEndX(matrix);
        int leftTopCodeEyeEndX = attr[1];
        int[] attr1 = new int[4];
        attr1[0] = attr[0];
        attr1[1] = attr[1];
        attr1[2] = attr[0];
        attr1[3] = attr[1];
        int[] leftTopinteriorEyeX = getCodeInteriorEyeStartXAndEndX(matrix,attr1);

        int[] attr2 = getRightTopCodeEyeEndX(matrix);
        int rightTopCodeEyeEndX = attr2[1];
        attr1 = new int[4];
        attr1[0] = attr2[1];
        attr1[1] = attr2[0];
        attr1[2] = attr[0];
        attr1[3] = attr[1];
        int[] rightTopinteriorEyeX = getCodeInteriorEyeStartXAndEndX(matrix,attr1);

        attr2 = getLeftBottomCodeEyeEndX(matrix);
        int leftBottomCodeEyeEndX = attr2[1];
        attr1[0] = attr[0];
        attr1[1] = attr[1];
        attr1[2] = attr2[1];
        attr1[3] = attr2[0];
        int[] leftBottominteriorEyeX = getCodeInteriorEyeStartXAndEndX(matrix,attr1);

        for (int y = 0; y < h; y++) {
            for (int x = 0; x < w; x++) {
                int colorInt = 0;
                if(matrix.get(x, y)){
                    if((x<=leftTopCodeEyeEndX)&&(y<=leftTopCodeEyeEndX) && (leftTopExternalColor!=null || leftTopInteriorColor!=null)){//左上 码眼
                        if(leftTopExternalColor!=null){
                            colorInt =leftTopExternalColor.getRGB();
                        }else{
                            colorInt = getColor(width,height,x,y,startColor,endColor).getRGB();
                        }
                        if(leftTopInteriorColor!=null && (x>=leftTopinteriorEyeX[0])&& (x<=leftTopinteriorEyeX[1]) && (y>=leftTopinteriorEyeX[0]) &&  (y<=leftTopinteriorEyeX[1])) {//左上 内码眼
                            colorInt =leftTopInteriorColor.getRGB();
                        }
                    }else if((x>=rightTopCodeEyeEndX)&&(y<=leftTopCodeEyeEndX) && (rightTopExternalColor!=null || rightTopInteriorColor!=null)){  // 右上码眼
                        if(rightTopExternalColor!=null){
                            colorInt =rightTopExternalColor.getRGB();
                        }else{
                            colorInt = getColor(width,height,x,y,startColor,endColor).getRGB();
                        }
                        if(rightTopInteriorColor!=null && (x>=rightTopinteriorEyeX[0])&& (x<=rightTopinteriorEyeX[1]) && (y>=leftTopinteriorEyeX[0]) &&  (y<=leftTopinteriorEyeX[1])) {//左上 内码眼
                            colorInt =rightTopInteriorColor.getRGB();
                        }

                    }else if((x<=leftTopCodeEyeEndX)&&(y>=leftBottomCodeEyeEndX) && (leftBottomExternalColor!=null || leftBottomInteriorColor!=null)){  // 左下码眼
                        if(leftBottomExternalColor!=null){
                            colorInt =leftBottomExternalColor.getRGB();
                        }else{
                            colorInt = getColor(width,height,x,y,startColor,endColor).getRGB();
                        }
                        if(leftBottomInteriorColor!=null && (x>=leftBottominteriorEyeX[0])&& (x<=leftBottominteriorEyeX[1]) && (y>=leftBottominteriorEyeX[2]) &&  (y<=(leftBottominteriorEyeX[2]+leftTopinteriorEyeX[1]-leftTopinteriorEyeX[0]))) {//左下 内码眼
                            colorInt =leftBottomInteriorColor.getRGB();
                        }
                    }else{
                        Color color = getColor(w,h,x,y,startColor,endColor);
                        colorInt = color.getRGB();
                    }
                }else{
                    if(impressionColor!=null){
                        colorInt = impressionColor.getRGB();
                    }else{
                        colorInt = -1;//白色
                    }
                }
                data[y * w + x] = colorInt;
            }
        }
        BufferedImage image = new BufferedImage(width, height,
                BufferedImage.TYPE_INT_RGB);
        image.getRaster().setDataElements(0, 0, width, height, data);
        return image;
    }

    /**
     * 获取第一个码眼结束 距离图片外边框 位置
     * @param matrix
     * @return
     */
    private static int[] getLeftTopCodeEyeEndX(BitMatrix matrix) {
        int w = matrix.getWidth();
        int h = matrix.getHeight();
        boolean flag1=true;
        int starx=0; //显示位置  xy相同
        int stopx=0; //结束位置 xy相同
        for (int y = 0; y < h; y++) {
            for (int x = 0; x < w; x++) {
                if(matrix.get(x, y)){
                    if(flag1){
                        starx =x;
                        flag1=false;
                    }
                }else{
                    if(flag1==false){
                        stopx =x-1;
                        break;
                    }
                }
            }
            if(flag1==false) {
                break;
            }
        }
        return new int[]{starx,stopx};
    }
    private static int[] getRightTopCodeEyeEndX(BitMatrix matrix) {
        int w = matrix.getWidth();
        int h = matrix.getHeight();
        boolean flag1=true;
        int starx=0; //显示位置  xy相同
        int stopx=0; //结束位置 xy相同
        for (int y = 0; y < h; y++) {
            for (int x = w-1; x >= 0; x--) {
                if(matrix.get(x, y)){
                    if(flag1){
                        starx =x;
                        flag1=false;
                    }
                }else{
                    if(flag1==false){
                        stopx =x+1;
                        break;
                    }
                }
            }
            if(flag1==false) {
                break;
            }
        }
        return new int[]{starx,stopx};    }

    /**
     * 获取第一个码眼结束 距离图片外边框 位置
     * @param matrix
     * @return
     */
    private static int[] getLeftBottomCodeEyeEndX(BitMatrix matrix) {
        int w = matrix.getWidth();
        int h = matrix.getHeight();
        boolean flag1=true;
        int starx=0; //显示位置  xy相同
        int stopx=0; //结束位置 xy相同
        for (int x = 0; x < w; x++) {
            for (int y = h-1; y >= 0; y--) {
                if(matrix.get(x, y)){
                    if(flag1){
                        starx =y;
                        flag1=false;
                    }
                }else{
                    if(flag1==false){
                        stopx =y+1;
                        break;
                    }
                }
            }
            if(flag1==false) {
                break;
            }
        }
        return new int[]{starx,stopx};
    }

    /**
     * 获取内码眼
     * @param matrix
     * @param startxAndEndx  [startx,stopx,starty,stopy]
     * @return [startx,stopx,starty]
     */
    private static int[] getCodeInteriorEyeStartXAndEndX(BitMatrix matrix,int[] startxAndEndx) {
        int startx = startxAndEndx[0];
        int stopx = startxAndEndx[1];
        int starty = startxAndEndx[2];
        int stopy = startxAndEndx[3];

        int interiorFlag = 0;
        int interiorStartx = 0;
        int interiorEndx = 0;
        int interiory = 0;

        for (int y = starty; y < stopy; y++) {
            for (int x = startx; x < stopx; x++) {
                if(x>(startx+(stopx-startx)/2D) && interiorStartx==0){
                    interiorFlag=0;
                    continue;
                }
                if(matrix.get(x, y)){
                    if(interiorFlag==1){
                        interiorStartx = x;
                        interiorFlag++;

                    }
                }else{
                    if(interiorFlag==0){
                        interiorFlag++;
                    }
                    if(interiorFlag==2){
                        interiorEndx = x-1;
                        interiory = y;
                        break;
                    }
                }
            }
            if(interiorFlag==2){
                break;
            }
        }
        return new int[]{interiorStartx,interiorEndx,interiory};
    }

    /**
     * 获取渐变色
     * @param w 宽度
     * @param h 高度
     * @param x 当前坐标点x
     * @param y 当前坐标点y
     * @param sc 渐变起始颜色
     * @param ec 渐变结束颜色
     * @return
     */
    private static Color getColor(int w, int h, int x, int y, Color sc, Color ec) {

        if(sc !=null && ec!=null){
            int sr = sc.getRed();
            int sg = sc.getGreen();
            int sb = sc.getBlue();

            int er = ec.getRed();
            int eg = ec.getGreen();
            int eb = ec.getBlue();


            //double rate = (y*1D)/h;
            double rate = (x*y*1D)/(w*h);
            int cr = Math.abs(sr-er);
            int cg = Math.abs(sg-eg);
            int cb = Math.abs(sb-eb);

            int nr = sr;
            if(sr < er){
                nr = new Double(sr+(cr*rate)).intValue();
            }else{
                nr = new Double(sr-(cr*rate)).intValue();
            }
            int ng = sg;
            if(sg < eg){
                ng = new Double(sg+(cg*rate)).intValue();
            }else{
                ng = new Double(sg-(cg*rate)).intValue();
            }
            int nb = sb;
            if(sb < eb){
                nb = new Double(sb+(cb*rate)).intValue();
            }else{
                nb = new Double(sb-(cb*rate)).intValue();
            }


            try{
                return new Color(nr, ng, nb);
            }catch (Exception e){
                return new Color(185, 177, 56);
            }
        }
        return new Color(0, 0, 0);

    }
}
